// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2012-2019. All rights reserved.
 * Description: support nand flash driver
 * Author: caizhiyong <caizhiyong@hisilicon.com>
 * Create: 2018-10-26
 */

#include <linux/init.h>
#include <linux/module.h>

#include "spi_ids.h"

#ifndef __tagtable
#define __tagtable(tag, fn) \
static const struct tagtable __tagtable_##fn __used __section(".taglist.init") = { tag, fn }
#endif

#define SPI_DRV_VERSION       "1.22"
#define  CONFIG_SPI_FLASH_BIG   1

#define DBG_MSG(_fmt, arg...)

#define DBG_BUG(fmt, args...) do { \
	pr_err("[%s:%d] BUG !!! " fmt, __func__, __LINE__, ##args); \
	while (1) \
		; \
} while (0)

#define SET_READ_STD(_dummy_, _size_, _clk_) \
	static struct spi_operation read_std_##_dummy_##_size_##_clk_ = { \
	SPI_IF_READ_STD, SPI_CMD_READ, _dummy_, _size_, _clk_ }

#define READ_STD(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_


#define SET_READ_FAST(_dummy_, _size_, _clk_) \
	static struct spi_operation read_fast_##_dummy_##_size_##_clk_ = { \
	SPI_IF_READ_FAST, SPI_CMD_FAST_READ, _dummy_, _size_, _clk_ }

#define READ_FAST(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_


#define SET_READ_DUAL(_dummy_, _size_, _clk_) \
	static struct spi_operation read_dual_##_dummy_##_size_##_clk_ = { \
	SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, _size_, _clk_ }

#define READ_DUAL(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_


#define SET_READ_QUAD(_dummy_, _size_, _clk_) \
	static struct spi_operation read_quad_##_dummy_##_size_##_clk_ = { \
	SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, _size_, _clk_ }

#define READ_QUAD(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_


#define SET_READ_DUAL_ADDR(_dummy_, _size_, _clk_) \
	static struct spi_operation read_dual_addr_##_dummy_##_size_##_clk_ = { \
	SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ }

#define READ_DUAL_ADDR(_dummy_, _size_, _clk_) read_dual_addr_##_dummy_##_size_##_clk_


#define SET_READ_QUAD_ADDR(_dummy_, _size_, _clk_) \
	static struct spi_operation read_quad_addr_##_dummy_##_size_##_clk_ = { \
	SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ }

#define READ_QUAD_ADDR(_dummy_, _size_, _clk_) read_quad_addr_##_dummy_##_size_##_clk_


#define SET_WRITE_STD(_dummy_, _size_, _clk_) \
	static struct spi_operation write_std_##_dummy_##_size_##_clk_ = { \
	SPI_IF_WRITE_STD, SPI_CMD_PP, _dummy_, _size_, _clk_ }

#define WRITE_STD(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_


#define SET_WRITE_DUAL(_dummy_, _size_, _clk_) \
	static struct spi_operation write_dual_##_dummy_##_size_##_clk_ = { \
	SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, _dummy_, _size_, _clk_ }

#define WRITE_DUAL(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_


#define SET_WRITE_QUAD(_dummy_, _size_, _clk_) \
	static struct spi_operation write_quad_##_dummy_##_size_##_clk_ = { \
	SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, _dummy_, _size_, _clk_ }

#define WRITE_QUAD(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_


#define SET_WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) \
	static struct spi_operation write_dual_addr_##_dummy_##_size_##_clk_ = { \
	SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ }

#define WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) write_dual_addr_##_dummy_##_size_##_clk_


#define SET_WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) \
	static struct spi_operation write_quad_addr_##_dummy_##_size_##_clk_ = { \
	SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ }

#define WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) write_quad_addr_##_dummy_##_size_##_clk_


#define SET_ERASE_SECTOR(_dummy_, _size_, _clk_) \
	static struct spi_operation erase_sector_##_dummy_##_size_##_clk_ = { \
	SPI_IF_ERASE_SECTOR, SPI_CMD_SE, _dummy_, _size_, _clk_ }

#define ERASE_SECTOR(_dummy_, _size_, _clk_) erase_sector_##_dummy_##_size_##_clk_


#define SET_ERASE_CHIP(_dummy_, _size_, _clk_) \
	static struct spi_operation erase_chip_##_dummy_##_size_##_clk_ = { \
	SPI_IF_ERASE_CHIP, SPI_CMD_BE, _dummy_, _size_, _clk_ }

#define ERASE_CHIP(_dummy_, _size_, _clk_) erase_chip_##_dummy_##_size_##_clk_

SET_READ_STD(0, INFINITE, 0);
SET_READ_STD(0, INFINITE, 20);
#ifdef CONFIG_SPI_FLASH_MID
SET_READ_STD(0, INFINITE, 32);
#endif
SET_READ_STD(0, INFINITE, 33);
SET_READ_STD(0, INFINITE, 40);
SET_READ_STD(0, INFINITE, 50);
SET_READ_STD(0, INFINITE, 54);
SET_READ_STD(0, INFINITE, 60);
#ifdef CONFIG_SPI_FLASH_BIG
SET_READ_STD(0, INFINITE, 80);
#endif
#ifdef CONFIG_SPI_FLASH_SMALL
SET_READ_STD(0, INFINITE, 66);
#endif

SET_READ_FAST(1, INFINITE, 50);
SET_READ_FAST(1, INFINITE, 64);
#ifdef CONFIG_SPI_FLASH_SMALL
SET_READ_FAST(1, INFINITE, 66);
#endif

#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_READ_FAST(1, INFINITE, 75);
#endif
SET_READ_FAST(1, INFINITE, 80);
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_READ_FAST(1, INFINITE, 86);
#endif
SET_READ_FAST(1, INFINITE, 104);
SET_READ_FAST(1, INFINITE, 108);

SET_READ_DUAL(1, INFINITE, 64);
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_READ_DUAL(1, INFINITE, 75);
#endif
SET_READ_DUAL(1, INFINITE, 80);
SET_READ_DUAL(1, INFINITE, 104);
SET_READ_DUAL(1, INFINITE, 108);
#ifdef CONFIG_SPI_FLASH_BIG
SET_READ_DUAL(3, INFINITE, 133);
#endif

#ifdef CONFIG_SPI_FLASH_MID
SET_READ_QUAD(1, INFINITE, 64);
#endif
SET_READ_QUAD(1, INFINITE, 70);
SET_READ_QUAD(1, INFINITE, 80);
SET_READ_QUAD(1, INFINITE, 108);
#ifdef CONFIG_SPI_FLASH_BIG
SET_READ_QUAD(3, INFINITE, 80);
SET_READ_QUAD(5, INFINITE, 108);
#endif

SET_READ_DUAL_ADDR(1, INFINITE, 56);
SET_READ_DUAL_ADDR(1, INFINITE, 64);
#ifdef CONFIG_SPI_FLASH_MID
SET_READ_DUAL_ADDR(2, INFINITE, 64);
#endif
SET_READ_DUAL_ADDR(0, INFINITE, 80);
#ifdef CONFIG_SPI_FLASH_MID
SET_READ_DUAL_ADDR(1, INFINITE, 80);
#endif
SET_READ_DUAL_ADDR(2, INFINITE, 108);

#ifdef CONFIG_SPI_FLASH_MID
SET_READ_QUAD_ADDR(5, INFINITE, 64);
#endif
SET_READ_QUAD_ADDR(2, INFINITE, 56);
SET_READ_QUAD_ADDR(2, INFINITE, 80);
SET_READ_QUAD_ADDR(5, INFINITE, 108);

SET_WRITE_STD(0, 256, 0);
SET_WRITE_STD(0, 256, 50);
SET_WRITE_STD(0, 256, 64);
#ifdef CONFIG_SPI_FLASH_SMALL
SET_WRITE_STD(0, 256, 66);
#endif
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_WRITE_STD(0, 256, 75);
#endif
SET_WRITE_STD(0, 256, 80);
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_WRITE_STD(0, 256, 86);
#endif
SET_WRITE_STD(0, 256, 104);
SET_WRITE_STD(0, 256, 108);

#ifdef CONFIG_SPI_FLASH_MID
SET_WRITE_DUAL(0, 256, 64);
#endif
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_WRITE_DUAL(0, 256, 75);
#endif
SET_WRITE_DUAL(0, 256, 108);

#ifdef CONFIG_SPI_FLASH_MID
SET_WRITE_QUAD(0, 256, 64);
#endif
SET_WRITE_QUAD(0, 256, 70);
SET_WRITE_QUAD(0, 256, 80);
SET_WRITE_QUAD(0, 256, 108);

#ifdef CONFIG_SPI_FLASH_MID
SET_WRITE_DUAL_ADDR(0, 256, 64);
#endif
SET_WRITE_DUAL_ADDR(0, 256, 108);

#ifdef CONFIG_SPI_FLASH_MID
SET_WRITE_QUAD_ADDR(0, 256, 64);
#endif
SET_WRITE_QUAD_ADDR(0, 256, 108);

SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0);
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 50);
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 64);
#ifdef CONFIG_SPI_FLASH_SMALL
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 66);
#endif
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 75);
#endif
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80);
#if defined(CONFIG_SPI_FLASH_MID) || defined(CONFIG_SPI_FLASH_SMALL)
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 86);
#endif
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104);
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 108);

static const char * const fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT", "%uT"};

static struct spi_info spi_info_def = {
	.name = "default_spi_flash",
	.id = {0},
	.id_len = 0,
	.chipsize = _16M,
	.erasesize = _256K,
	.addrcycle = 3,
	.read = {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
	.write = {&WRITE_STD(0, 256, 104), 0},
	.erase = {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0},
	.BP_bitmask = 0x07,
};

static struct spi_info spi_info_table[] = {
	/* boot tag */
	{"null", {0, 0, 0}, 3, 0, 0, 0, {0}, {0}, {0}, 0},
#ifdef CONFIG_SPI_FLASH_SMALL
	/* name        id                id_len chipsize(Bytes) erasesize */
	{
		"at25fs010",  {0x1f, 0x66, 0x01}, 3,  _128K,  _32K, 3,
		{&READ_STD(0, INFINITE, 0), 0},
		{&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"at25fs040",  {0x1f, 0x66, 0x04}, 3,  _512K,  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"at25df041a", {0x1f, 0x44, 0x01}, 3,  _512K,  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"at25df641",  {0x1f, 0x48, 0x00}, 3,  _8M,    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	{
		"at26f004",   {0x1f, 0x04, 0x00}, 3,  _512K,  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"at26df081a", {0x1f, 0x45, 0x01}, 3,  _1M,    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"at26df161a", {0x1f, 0x46, 0x01}, 3,  _2M,    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"at26df321",  {0x1f, 0x47, 0x01}, 3,  _4M,    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	/* Macronix */
	{
		"mx25l4005a",  {0xc2, 0x20, 0x13}, 3, _512K,  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"MX25L8006E",
		{0xc2, 0x20, 0x14}, 3, _1M, _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 86),
			&READ_DUAL(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 86), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 86), 0}, 0x07
	}, {
		"MX25L1606E",  {0xc2, 0x20, 0x15}, 3, _2M,    _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 86),
			&READ_DUAL(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 86), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 86), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"mx25l3205d",  {0xc2, 0x20, 0x16}, 3, _4M, _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"MX25L6406E",  {0xc2, 0x20, 0x17}, 3, _8M,    _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 86),
			&READ_DUAL(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 86), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 86), 0}, 0x07},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"GD25Q127C",  {0xc8, 0x40, 0x18}, 3, _16M, _64K, 3,
		{&READ_STD(0, INFINITE, 60), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 104), 0},
		/* BP_bitmask need to comfirm */
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"MX25L12835F", {0xc2, 0x20, 0x18}, 3, _16M, _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 108), 0},
		{&WRITE_STD(0, 256, 108), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 108), 0}, 0x0f
	}, {
		"MX25L25635E", {0xc2, 0x20, 0x19}, 3, _32M, _64K, 4,
		{&READ_STD(0, INFINITE, 40/*50*/),
			&READ_FAST(1, INFINITE, 64/*80*/),
			&READ_DUAL_ADDR(1, INFINITE, 56/*70*/),
			&READ_QUAD_ADDR(2, INFINITE, 56/*70*/), 0},
		{&WRITE_STD(0, 256, 64/*80*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 64/*80*/), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	{
		"mx25l1655d",  {0xc2, 0x26, 0x15}, 3, _2M,    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"mx25l12855e", {0xc2, 0x26, 0x18}, 3, _16M,   _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	{
		"s25sl004a", {0x01, 0x02, 0x12}, 3, (_64K * 8),   _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"s25sl008a", {0x01, 0x02, 0x13}, 3, (_64K * 16),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"s25sl016a", {0x01, 0x02, 0x14}, 3, (_64K * 32),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"S25FL064P", {0x01, 0x02, 0x16, 0x4d}, 4, (_64K * 128), _64K, 3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"s25sl064a", {0x01, 0x02, 0x16}, 3, (_64K * 128), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"S25FL032P", {0x01, 0x02, 0x15, 0x4d}, 4, (_64K * 64),  _64K, 3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80),
			&READ_DUAL_ADDR(0, INFINITE, 80),
			&READ_QUAD_ADDR(2, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"S25FL032A", {0x01, 0x02, 0x15}, 3, (_64K * 64),  _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 50), 0},
		{&WRITE_STD(0, 256, 50), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 50), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"S25FL128P-0", {0x01, 0x20, 0x18, 0x03, 0x00}, 5, (_256K * 64),  _256K, 3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
		{&WRITE_STD(0, 256, 104), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"S25FL128P-1", {0x01, 0x20, 0x18, 0x03, 0x01}, 5, (_64K * 256),  _64K, 3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
		{&WRITE_STD(0, 256, 104), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"S25FL129P0", {0x01, 0x20, 0x18, 0x4d, 0x00}, 5, (_256K * 64),  _256K, 3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80),
			&READ_DUAL_ADDR(0, INFINITE, 80),
			&READ_QUAD_ADDR(2, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"S25FL129P1", {0x01, 0x20, 0x18, 0x4d, 0x01}, 5, (_64K * 256),  _64K,  3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 64), &READ_QUAD(1, INFINITE, 80),
			&READ_DUAL_ADDR(0, INFINITE, 80), &READ_QUAD_ADDR(2, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"S25FL256S", {0x01, 0x02, 0x19, 0x4d}, 4, _32M,  _64K, 3,
		{&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"S25FL128l", {0x01, 0x60, 0x18}, 3, (_64K * 256),  _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 108),
			&READ_DUAL(3, INFINITE, 133), &READ_QUAD(5, INFINITE, 108), 0},
		{&WRITE_STD(0, 256, 50), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 50), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
	{
		"sst25vf040b", {0xbf, 0x25, 0x8d}, 3, (_64K * 8),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25vf080b", {0xbf, 0x25, 0x8e}, 3, (_64K * 16), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25vf016b", {0xbf, 0x25, 0x41}, 3, (_64K * 32), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25vf032b", {0xbf, 0x25, 0x4a}, 3, (_64K * 64), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25wf512",  {0xbf, 0x25, 0x01}, 3, (_64K * 1),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25wf010",  {0xbf, 0x25, 0x02}, 3, (_64K * 2),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25wf020",  {0xbf, 0x25, 0x03}, 3, (_64K * 4),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"sst25wf040",  {0xbf, 0x25, 0x04}, 3, (_64K * 8),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},

	/* ST Microelectronics -- newer production may have feature updates */
	{
		"m25p05",  {0x20, 0x20, 0x10}, 3, (_32K * 2), _32K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25p10",  {0x20, 0x20, 0x11}, 3, (_32K * 4), _32K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25p20",  {0x20, 0x20, 0x12}, 3, (_64K * 4),   _64K,  3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25p40",  {0x20, 0x20, 0x13}, 3, (_64K * 8),   _64K,  3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25p80",  {0x20, 0x20, 0x14}, 3, (_64K * 16),  _64K,  3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25p16",  {0x20, 0x20, 0x15}, 3, (_64K * 32),  _64K,  3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"M25P32",  {0x20, 0x20, 0x16, 0x10}, 4, _4M, _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 75), 0},
		{&WRITE_STD(0, 256, 75), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 75), 0}, 0x07
	}, {
		"m25p64",  {0x20, 0x20, 0x17}, 3, (_64K * 128), _64K,  3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"N25Q064", {0x20, 0xba, 0x17}, 3, (_64K * 128), _64K, 3,
		{&READ_STD(0, INFINITE, 32/*54*/), &READ_FAST(1, INFINITE, 64/*108*/), 0},
		{&WRITE_STD(0, 256, 64/*108*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 64/*108*/), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"M25P128", {0x20, 0x20, 0x18}, 3, _16M, _256K, 3,
		{&READ_STD(0, INFINITE, 20), &READ_FAST(1, INFINITE, 50), 0},
		{&WRITE_STD(0, 256, 50), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 50), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	{
		"m45pe10", {0x20, 0x40, 0x11}, 3, (_64K * 2),   _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m45pe80", {0x20, 0x40, 0x14}, 3, (_64K * 16),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m45pe16", {0x20, 0x40, 0x15}, 3, (_64K * 32),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25pe80", {0x20, 0x80, 0x14}, 3, (_64K * 16), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"m25pe16", {0x20, 0x80, 0x15}, 3, (_64K * 32), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"N25Q032", {0x20, 0xba, 0x16}, 3, (_64K * 64), _64K, 3,
		{&READ_STD(0, INFINITE, 32/*54*/),
			&READ_FAST(1, INFINITE, 64/*108*/),
			&READ_DUAL(1, INFINITE, 64/*108*/),
			&READ_QUAD(1, INFINITE, 64/*108*/),
			&READ_DUAL_ADDR(2, INFINITE, 64/*108*/),
			&READ_QUAD_ADDR(5, INFINITE, 64/*108*/), 0},
		{&WRITE_STD(0, 256, 64/*108*/),
			&WRITE_DUAL(0, 256, 64/*108*/),
			&WRITE_QUAD(0, 256, 64/*108*/),
			&WRITE_DUAL_ADDR(0, 256, 64/*108*/),
			&WRITE_QUAD_ADDR(0, 256, 64/*108*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 64/*108*/), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"mt25ql128ab", {0x20, 0xba, 0x18, 0x10, 0x40, 0x00},
		6, (_64K * 256), _64K, 3,
		{&READ_STD(0, INFINITE, 54), &READ_FAST(1, INFINITE, 108),
			&READ_DUAL(1, INFINITE, 108), &READ_QUAD(1, INFINITE, 108),
			&READ_DUAL_ADDR(2, INFINITE, 108),
			&READ_QUAD_ADDR(5, INFINITE, 108), 0},
		{&WRITE_STD(0, 256, 108), &WRITE_DUAL(0, 256, 108),
			&WRITE_QUAD(0, 256, 108), &WRITE_DUAL_ADDR(0, 256, 108),
			&WRITE_QUAD_ADDR(0, 256, 108), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 108), 0}, 0x0f
	}, {
		"N25Q128",   {0x20, 0xba, 0x18}, 3, (_64K * 256), _64K, 3,
		{&READ_STD(0, INFINITE, 54), &READ_FAST(1, INFINITE, 108),
			&READ_DUAL(1, INFINITE, 108), &READ_QUAD(1, INFINITE, 108),
			&READ_DUAL_ADDR(2, INFINITE, 108),
			&READ_QUAD_ADDR(5, INFINITE, 108), 0},
		{&WRITE_STD(0, 256, 108), &WRITE_DUAL(0, 256, 108),
			&WRITE_QUAD(0, 256, 108), &WRITE_DUAL_ADDR(0, 256, 108),
			&WRITE_QUAD_ADDR(0, 256, 108), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 108), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	{
		"M25PX16",  {0x20, 0x71, 0x15}, 3, (_64K * 32),  _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 75),
			&READ_DUAL(1, INFINITE, 75), 0},
		{&WRITE_STD(0, 256, 75), &WRITE_DUAL(0, 256, 75), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 75), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"M25PX32", {0x20, 0x71, 0x16}, 3, (_64K * 64),  _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 75),
			&READ_DUAL(1, INFINITE, 75), 0},
		{&WRITE_STD(0, 256, 75), &WRITE_DUAL(0, 256, 75), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 75), 0}, 0x07
	}, {
		"m25px64",  {0x20, 0x71, 0x17}, 3, (_64K * 128), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
	{
		"w25x10",  {0xef, 0x30, 0x11}, 3, (_64K * 2),    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"w25x20",  {0xef, 0x30, 0x12}, 3, (_64K * 4),    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"w25x40",  {0xef, 0x30, 0x13}, 3, (_64K * 8),    _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"w25x80",  {0xef, 0x30, 0x14}, 3, (_64K * 16),   _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"w25x16",  {0xef, 0x30, 0x15}, 3, (_64K * 32), _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"w25x32",  {0xef, 0x30, 0x16}, 3, (_64K * 64),   _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	}, {
		"w25x64",  {0xef, 0x30, 0x17}, 3, (_64K * 128),  _64K, 3,
		{&READ_STD(0, INFINITE, 0), 0},  {&WRITE_STD(0, 256, 0), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 0), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	{
		"W25Q80BV",  {0xef, 0x40, 0x14}, 3, (_64K * 16),   _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 80),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80), 0}, 0x07
	}, {
		"W25Q16(B/C)V/S25FL016K",  {0xef, 0x40, 0x15}, 3, (_64K * 32),   _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 80),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{
		"W25Q32BV",  {0xef, 0x40, 0x16}, 3, (_64K * 64),   _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 80),
			&READ_DUAL(1, INFINITE, 80), &READ_QUAD(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80), &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"W25Q128FV", {0xEF, 0x40, 0x18}, 3, _16M, _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 104), &READ_QUAD(1, INFINITE, 70), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 70), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	}, {
		"W25Q128JW", {0xEF, 0x60, 0x18}, 3, _16M, _64K, 3,
		{&READ_STD(0, INFINITE, 33), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 104), &READ_QUAD(1, INFINITE, 70), 0},
		{&WRITE_STD(0, 256, 104), &WRITE_QUAD(0, 256, 70), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_SMALL
	/* Eon -- fit clock frequency of RDSR instruction*/
	{
		"EN25F80", {0x1c, 0x31, 0x14}, 3, (_64K * 16),  _64K, 3,
		{&READ_STD(0, INFINITE, 66), &READ_FAST(1, INFINITE, 66/*100*/), 0},
		{&WRITE_STD(0, 256, 66/*100*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 66/*100*/), 0}, 0x07
	}, {
		"EN25F16", {0x1c, 0x31, 0x15}, 3, _2M,  _64K, 3,
		{&READ_STD(0, INFINITE, 66), &READ_FAST(1, INFINITE, 66/*100*/), 0},
		{&WRITE_STD(0, 256, 66/*100*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 66/*100*/), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_MID
	{"EN25Q32B", {0x1c, 0x30, 0x16}, 3, (_64K * 64),  _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 80/*104*/),
			&READ_DUAL(1, INFINITE, 80), &READ_DUAL_ADDR(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80/*104*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80/*104*/), 0}, 0x07
	}, {
		"EN25Q64", {0x1c, 0x30, 0x17}, 3, (_64K * 128),  _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 80/*104*/),
			&READ_DUAL(1, INFINITE, 80), &READ_DUAL_ADDR(1, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80/*104*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80/*104*/), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"EN25Q128", {0x1c, 0x30, 0x18}, 3, (_64K * 256),  _64K, 3,
		{&READ_STD(0, INFINITE, 50), &READ_FAST(1, INFINITE, 80/*104*/),
			&READ_DUAL(1, INFINITE, 64/*80*/),
			&READ_DUAL_ADDR(1, INFINITE, 64/*80*/), 0},
		{&WRITE_STD(0, 256, 80/*104*/), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80/*104*/), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"is25lp128f", {0x9d, 0x60, 0x18}, 3, (_64K * 256),  _64K, 3,
		{&READ_STD(0, INFINITE, 80), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 104), &READ_QUAD(3, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80), 0, &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80), 0}, 0x07
	},
#endif

#ifdef CONFIG_SPI_FLASH_BIG
	{
		"25F128F", {0x0B, 0x40, 0x18}, 3, (_64K * 256),  _64K, 3,
		{&READ_STD(0, INFINITE, 80), &READ_FAST(1, INFINITE, 104),
			&READ_DUAL(1, INFINITE, 104), &READ_QUAD(3, INFINITE, 80), 0},
		{&WRITE_STD(0, 256, 80), 0, &WRITE_QUAD(0, 256, 80), 0},
		{&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 80), 0}, 0x07
	},
#endif
	{0, {0}, 0, 0, 0, 0, {0}, {0}, {0}, 0},
};

static char *int_to_size(unsigned long long size)
{
	int i;
	static char buffer[20];

	for (i = 0; (i < 5) && !(size & 0x3FF) && size; i++)
		size = (size >> 10);

	snprintf(buffer, 20, fmt[i], size);
	return buffer;
}

struct spi_info *spi_serach_ids(unsigned char *ids, unsigned int len)
{
	struct spi_info *info = NULL;
	struct spi_info *fit_info = NULL;

	if (len == 0)
		return NULL;

	for (info = spi_info_table; info->name; info++) {
		if (memcmp(info->id, ids, info->id_len))
			continue;

		if ((!fit_info) || (fit_info->id_len < info->id_len))
			fit_info = info;
	}

	if (!fit_info)
		/* use default spi flash type */
		fit_info = &spi_info_def;

	return fit_info;
}

void spi_search_rw(struct spi_info *spiinfo, struct spi_operation *spiop_rw,
	unsigned int iftype, unsigned int max_dummy, int is_read)
{
	int i = 0;
	struct spi_operation **spiop, **fitspiop;

	fitspiop = (is_read ? spiinfo->read : spiinfo->write);
	for (spiop = fitspiop;
		(*spiop) && i < MAX_SPI_OP; spiop++, i++) {
		DBG_MSG("dump[%d] %s iftype:0x%02X\n", i,
			(is_read ? "read" : "write"), (*spiop)->iftype);

		if (((*spiop)->iftype & iftype) && ((*spiop)->dummy <= max_dummy) &&
			(*fitspiop)->iftype < (*spiop)->iftype)
			fitspiop = spiop;
	}
	memcpy(spiop_rw, (*fitspiop), sizeof(struct spi_operation));
}

void spi_get_erase(struct spi_info *spiinfo, struct spi_operation *spiop_erase,
	unsigned int *erasesize)
{
	int i;

	(*erasesize) = spiinfo->erasesize;
	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spiinfo->erase[i] == NULL)
			break;

		memcpy(&spiop_erase[i], spiinfo->erase[i],
			sizeof(struct spi_operation));

		switch (spiop_erase[i].size) {
		case SPI_IF_ERASE_SECTOR:
			spiop_erase[i].size = spiinfo->erasesize;
			break;
		case SPI_IF_ERASE_CHIP:
			spiop_erase[i].size = spiinfo->chipsize;
			break;
		default:
			break;
		}
		DBG_MSG("erase[%d]->erasesize:%s, cmd:0x%02X\n",
			i, int_to_size(spiop_erase[i].size), spiop_erase[i].cmd);

		if ((int)(spiop_erase[i].size) < _2K) {
			DBG_BUG("erase block size mistaken: spi->erase[%d].size:%s\n",
				i, int_to_size(spiop_erase[i].size));
		}

		if (spiop_erase[i].size < (*erasesize))
			(*erasesize) = spiop_erase[i].size;
	}
}

struct spi_tag {
	char name[16];

	unsigned char id[8];
	unsigned int id_len;

	unsigned long chipsize;
	unsigned int erasesize;
	unsigned int addrcycle;

	struct spi_operation read[MAX_SPI_OP];
	struct spi_operation write[MAX_SPI_OP];
	struct spi_operation erase[MAX_SPI_OP];
};

static int __init parse_spi_id(const struct tag *input_tag)
{
	int i;
	static struct spi_tag spitag[1];
	struct spi_info *spiinfo = spi_info_table;

	if (input_tag->hdr.size != ((sizeof(struct tag_header) + sizeof(struct spi_tag)) >> 2)) {
		pr_warn("%s(%d):tag->hdr.size(%d) too small.\n",
			__func__, __LINE__, input_tag->hdr.size);
		return 0;
	}
	memset(spiinfo, 0, sizeof(struct spi_info));
	memcpy(spitag, &input_tag->u, sizeof(struct spi_tag));

	spiinfo->name = spitag->name;

	memcpy(spiinfo->id, spitag->id, 8);
	spiinfo->id_len = spitag->id_len;

	spiinfo->chipsize = spitag->chipsize;
	spiinfo->erasesize = spitag->erasesize;
	spiinfo->addrcycle = spitag->addrcycle;

	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spitag->read[i].iftype)
			spiinfo->read[i] = &spitag->read[i];
	}
	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spitag->write[i].iftype)
			spiinfo->write[i] = &spitag->write[i];
	}
	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spitag->erase[i].iftype)
			spiinfo->erase[i] = &spitag->erase[i];
	}

	pr_info("SPI TAG: hdr.tag: 0x%08X, hdr.size: %d\n",
		input_tag->hdr.tag, input_tag->hdr.size);
	pr_info("(%dByte): 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
		spitag->id_len,
		spitag->id[0], spitag->id[1], spitag->id[2], spitag->id[3],
		spitag->id[4], spitag->id[5], spitag->id[6], spitag->id[7]);
	pr_info("Block:%sB ", int_to_size(spitag->erasesize));
	pr_info("Chip:%sB ", int_to_size(spitag->chipsize));
	pr_info("AddrCycle:%d ", spitag->addrcycle);
	pr_info("Name:(%s)", spitag->name);
	pr_info("\n");
	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spitag->read[i].iftype) {
			pr_info("R %d: ", i + 1);
			pr_info("IF Type:0x%02X ", spitag->read[i].iftype);
			pr_info("CMD:0x%02X ",     spitag->read[i].cmd);
			pr_info("Dummy:%d ",       spitag->read[i].dummy);
			if (spitag->read[i].size == INFINITE)
				pr_info("Size:-1      ");
			else
				pr_info("Size:%6sB ", int_to_size(spitag->read[i].size));
			pr_info("Clock:%dMHz ",    spitag->read[i].clock);
			pr_info("\n");
		}
	}
	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spitag->write[i].iftype) {
			pr_info("W %d: ", i + 1);
			pr_info("IF Type:0x%02X ", spitag->write[i].iftype);
			pr_info("CMD:0x%02X ", spitag->write[i].cmd);
			pr_info("Dummy:%d ",   spitag->write[i].dummy);
			pr_info("Size:%6sB ",  int_to_size(spitag->write[i].size));
			pr_info("Clock:%dMHz ", spitag->write[i].clock);
			pr_info("\n");
		}
	}
	for (i = 0; i < MAX_SPI_OP; i++) {
		if (spitag->erase[i].iftype) {
			pr_info("E %d: ", i + 1);
			pr_info("IF Type:0x%02X ", spitag->erase[i].iftype);
			pr_info("CMD:0x%02X ",     spitag->erase[i].cmd);
			pr_info("Dummy:%d ",       spitag->erase[i].dummy);
			pr_info("Size:0x%02X ",    spitag->erase[i].size);
			pr_info("Clock:%dMHz ",    spitag->erase[i].clock);
			pr_info("\n");
		}
	}

	return 0;
}

/* turn to ascii is "S_ID" */
__tagtable(SPI_IDS_TAG_SPI_PARTITIONS, parse_spi_id);

MODULE_IMPORT_NS(HW_RTOS_NS);
